/**
******************************************************************************************
* @file Electrician.c
* @brief Management of the ambient light bricklet.
* @date 29 mars 2015
* @author Benjamin Lancinot
*****************************************************************************************/
/* COPYRIGHT ****************************************************************************
*****************************************************************************************
* Copyright (c) 2015, Audrey LOISEAU and Benjamin LANCINOT.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
*    list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
*    this list of conditions and the following disclaimer in the documentation
*    and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
********************************************************************************************/

#include "Electrician.h"

/*******************************************************************************************
* PROTOTYPES
********************************************************************************************/

float giveLuminosity(float, float, uint16_t);

void cb_illuminance(uint16_t, void *);

void Electrician_initThreshold(Electrician *);

void Electrician_destroyUtilities(Electrician*);

void Electrician_destroy(Electrician**);

int Electrician_registerObserver(Electrician*, Observer* );

int Electrician_unregisterObserver(Electrician*, Observer*);

/*******************************************************************************************
* FUNCTIONS
********************************************************************************************/

/**
 * @brief Constructor
 * @param ipcon Ipconnection for link bricklet to the rest of bricks/bricklets
 * @return Pointer to a new Electrician
 **/
Electrician* Electrician_create(IPConnection ipcon){
	Electrician* This = (Electrician *) malloc(sizeof(*This));

	//Creation of ambient light sensor
	AmbientLight al_sensor;
	ambient_light_create(&al_sensor, "mbs", &ipcon);
	This->lightSensor = al_sensor;

        //Init functions
	This->initThreshold=Electrician_initThreshold;
        This->destroy = Electrician_destroy;
	This->subject = subjectNew(This, 0);
	This->registerObserver = Electrician_registerObserver;
	This->unregisterObserver = Electrician_unregisterObserver;

        //Init variable
        This->luminosity = 0;
        This->luminosityThreshold[0]= 50;
        This->luminosityThreshold[1] = 65;


	return This;

}

/**
 * @brief Using for determining the luminosity according to thresholds
 * @param dimThreshold Value of the low threshold of luminosity
 * @param brightThreshold Value of the high threshold of luminosity
 * @param  illuminance Value of the illuminance given by the bricklet
 * @return The good value of luminosity (dark, dim or bright)
 **/
float giveLuminosity(float dimThreshold, float brightThreshold, uint16_t illuminance){
	//the luminosity is dark
	if  (illuminance/10.0 <= dimThreshold){
		return 1;
        }
	//The luminosity is dim dark
	if  (illuminance/10.0 <=brightThreshold && illuminance/10.0 >dimThreshold ){
		return 2;
	}
        //The luminosity is bright
	else {
		return 3;
	}
}


/**
 * @brief Use by the callback function for notify observers and recover the good luminosity state
 * @param illuminance Value of the illuminance given by the bricklet
 * @param user_data Pointer to the wanted Electrician for recover thresholds
 **/
void cb_illuminance(uint16_t illuminance, void *user_data) {
	float newLuminosity;
	Electrician* This;
	This = (Electrician*) user_data;

        //Recover the new luminosity
        newLuminosity = giveLuminosity(This->luminosityThreshold[0], This->luminosityThreshold[1], illuminance);

        //notify observers when the luminosity changed
	if ( This->luminosity != newLuminosity){
		This->luminosity = newLuminosity;
		This->subject->notifyObservers(This->subject);
        }
}

/**
 * @brief Init the electrician and callback function
 * @param This Pointer to the wanted Electrician
 **/
void Electrician_initThreshold(Electrician *This){
	// Create the callback. It's called every second
	ambient_light_set_illuminance_callback_period(&This->lightSensor, 1000);

	// Register illuminance callback to function cb_illuminance
	ambient_light_register_callback(&This->lightSensor,
	                                AMBIENT_LIGHT_CALLBACK_ILLUMINANCE,
	                                (void *)cb_illuminance,
				        (void *) This);

        //Recover the first luminosity
        uint16_t illuminance;
        ambient_light_get_illuminance(&This->lightSensor, &illuminance);

        This->luminosity = giveLuminosity(This->luminosityThreshold[0], This->luminosityThreshold[1], illuminance);
}


/**
 * @brief Destroy all of the components of Electrician
 * @param This Pointer to the wanted Electrician
 **/
void Electrician_destroyUtilities(Electrician* This){
	This->subject->destroy(This->subject);
        ambient_light_destroy(&This->lightSensor);
}

/**
 * @brief Destructor
 * @param This Double pointer to the wanted Electrician
 **/
void Electrician_destroy(Electrician** This){

        Electrician_destroyUtilities(* This);
	if (NULL != *This) {
		free(*This);
		*This = NULL;
	}
}


/**
 * @brief Proxy method for Register an observer for the Electrician object.
 * @param This Pointer to the wanted Electrician
 * @param This Pointer to the wanted observer
 * @return The result of the development of the registration (0 = GOOD and 1 = NOT GOOD)
 **/
int Electrician_registerObserver(Electrician* This, Observer* observer) {
	return This->subject->registerObserver(This->subject, observer);
}

/**
 * @brief Proxy method for Unregister observer for the Electrician object.
 * @param This Pointer to the wanted Electrician
 * @param This Pointer to the wanted observer
 * @return The result of the development of the unregistration (0 = GOOD and 1 = NOT GOOD)
 **/
int Electrician_unregisterObserver(Electrician* This, Observer* observer) {
	return This->subject->unregisterObserver(This->subject, observer);
}
